package com.bosch.in.rtp1.ept.action;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.List;
import java.util.Random;

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONArray;
import net.sf.json.JSONSerializer;

import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.MappingDispatchAction;

import com.bosch.in.rtp1.ept.constant.ModifiedLinksContanst;
import com.bosch.in.rtp1.ept.manager.LinkListManager;
import com.bosch.in.rtp1.ept.model.dto.JsonFactors;
import com.bosch.in.rtp1.ept.model.dto.Link;
import com.bosch.in.rtp1.ept.util.ConverterUtility;
import com.bosch.in.rtp1.ept.util.CopyToNetworkFolder;
import com.bosch.in.rtp1.ept.util.ManagerFactory;

public class ModifiedLinksAction extends MappingDispatchAction{
	
	private static final Logger LOGGER = Logger.getLogger(ModifiedLinksAction.class);
	
	/**
     * public folder is used to store files from external links
     */
    private static final String SHARED_FOLDER = "sharedFolder/";
    /**
     * buffer size is used to read file content
     */
    private static final int BUFSIZE = 1024;
    
	
	/**
	 * @param mapping
	 * @param form
	 * @param request
	 * @param response
	 * @return
	 * @throws SQLException
	 * @throws IOException
	 */
	public ActionForward loadLinkList(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws SQLException, IOException {
		LOGGER.debug("Begin ModifiedLinksAction.loadSkippedCondition");

		// get LinkListManager instance
		LinkListManager linkListManager = ManagerFactory.getInstance()
				.getLinkListManager();

		// retrieve FXFactor list according to the conditions
		List<Link> links = linkListManager.getLinkList();

		// convert returned data in kind of JSON format which Jqgrid supports
		JSONArray cellarray = ConverterUtility.convertLinksToRows(links);
		JsonFactors jsonFXFactors = new JsonFactors();
		jsonFXFactors.setRows(cellarray);

		// write data to output stream with MIME type is text
		String jsonResponse = JSONSerializer.toJSON(jsonFXFactors).toString();
		response.setContentType("text/text;charset=utf-8");
		response.setHeader("cache-control", "no-cache");
		PrintWriter out = response.getWriter();
		out.println(jsonResponse);
		out.flush();
		LOGGER.debug("End ModifiedLinksAction.loadSkippedCondition");
		return mapping.findForward("success");
	}

	/**
	 * Update/Insert Link object into database based on status of that object.
	 * 
	 * @param mapping
	 *            The ActionMapping used to select this instance
	 * @param form
	 *            The optional ActionForm bean for this request (if any)
	 * @param request
	 *            The HTTP request we are processing
	 * @param response
	 *            The HTTP response we are creating
	 * @exception Exception
	 *                if an exception occurs
	 * @return the ActionForward to forward control to
	 */
	public ActionForward updateLinks(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		LOGGER.debug("Begin ModifiedLinksAction.updateLinks");

		// get LinkListManager instance
		LinkListManager linkListManager = ManagerFactory.getInstance()
				.getLinkListManager();

		// get fx factor list from request
		List<Link> links = ConverterUtility.convertToLinks(request);

		// update links order
		updateLinkOrder(links);

		// update database
		String errorMsg = linkListManager.updateLinks(links);
		if (!errorMsg.isEmpty()) {
			response.setContentType("text/html;charset=utf-8");
			PrintWriter out = response.getWriter();
			out.println(errorMsg);
		}

		LOGGER.debug("End ModifiedLinksAction.updateLinks");

		return null;
	}

	/**
	 * If user click on external link (link to file only) in FireFox browser, the request will come 
	 * to this method which will download external file into certain folder in web application. Then
	 * it will read stream data from this cached file and return to client request.
	 * @param mapping
	 *            The ActionMapping used to select this instance
	 * @param form
	 *            The optional ActionForm bean for this request (if any)
	 * @param request
	 *            The HTTP request we are processing
	 * @param response
	 *            The HTTP response we are creating
	 * @exception Exception
	 *                if an exception occurs
	 * @return the ActionForward to forward control to
	 * @throws SQLException
	 */
	public ActionForward openExternalLink(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws SQLException, IOException {
		LOGGER.debug("Begin openExternalLink");

		String fileLink = request.getParameter(ModifiedLinksContanst.FILE_LINK);
		fileLink = ConverterUtility.convertFileProtocal(fileLink);
		// Get source file
		File fileFrom = new File(fileLink);
		
		// Rename file name to avoid concurrent access
		String fileName = FilenameUtils.getName(fileLink);
		String fileExtenstion = FilenameUtils.getExtension(fileName);
		fileName = FilenameUtils.removeExtension(fileName);
		long timestamp = Calendar.getInstance().getTimeInMillis();
		Random generator = new Random();
		int random = generator.nextInt(1000);
		String desFilePath = SHARED_FOLDER + fileName + timestamp + random
				+ ModifiedLinksContanst.EXTENSION_SEPARATOR + fileExtenstion;
		
		// Create temporary file for downloading
		desFilePath = this.getServlet().getServletContext()
				.getRealPath(desFilePath);
		File fileTo = new File(desFilePath);
		
		// Caching source file to web application
		String error = CopyToNetworkFolder.copyFile(fileFrom, fileTo);
		if(error.isEmpty()) {
			// return file stream to client
			downloadFile(request, response, desFilePath);
			// delete temporary file
			fileTo.delete();
		} else {
			return mapping.findForward("fail");
		}
		
		LOGGER.debug("End openExternalLink");
		return null;

	}

	/**
	 * Reads a file on the server and sends it to the user as a stream of bytes.
	 * @param request	HttpServletRequest	
	 * @param response	HttpServletResponse
	 * @param filePath	absolute path to file on server
	 * @throws IOException
	 */
	private void downloadFile(HttpServletRequest request,
			HttpServletResponse response, String filePath) throws IOException {
		File file = new File(filePath);
		int length = 0;
		ServletOutputStream outStream = response.getOutputStream();
		ServletContext context = this.getServlet().getServletContext();
		String mimetype = context.getMimeType(filePath);

		// sets response content type
		if (mimetype == null) {
			mimetype = "application/octet-stream";
		}
		response.setContentType(mimetype);
		response.setContentLength((int) file.length());
		String fileName = (new File(filePath)).getName();

		// sets HTTP header
		response.setHeader("Content-Disposition", "attachment; filename=\""
				+ fileName + "\"");

		byte[] byteBuffer = new byte[BUFSIZE];
		DataInputStream in = new DataInputStream(new FileInputStream(file));

		// reads the file's bytes and writes them to the response stream
		while ((in != null) && ((length = in.read(byteBuffer)) != -1)) {
			outStream.write(byteBuffer, 0, length);
		}

		in.close();
		outStream.close();
	}

	private void updateLinkOrder(List<Link> links) {
		int order = 1;
		int i = 0;
		while (i < links.size()) {
			Link link = links.get(i);
			if (link.getStatus() != ModifiedLinksContanst.DELETED_LINK_STATUS) {
				link.setOrder(order);
				++order;
			}
			++i;
		}
	}

}
